FastAi's Course-v3 Method

Google images search (type:face)

"jisoo blackpink" "jennie blackpink" "Rosé blackpink" "lisa blackpink"

Scroll down to load more images until "Show more results" button comes up. Paste this into console:

urls = Array.from(document.querySelectorAll('.rg_di .rg_meta')).map(el=>JSON.parse(el.textContent).ou); window.open('data:text/csv;charset=utf-8,' + escape(urls.join('\n')));

Save the URLs as a csv.

DownAlbum.

https://www.pinterest.ca/lulamulala/blackpink-jisoo/social-media/

https://www.pinterest.ca/lulamulala/blackpink-jisoo/photoshoot/

https://www.pinterest.ca/lulamulala/blackpink-jennie/social-media/

https://www.pinterest.ca/lulamulala/blackpink-jennie/photoshoot/

https://www.pinterest.ca/lulamulala/blackpink-rose/social-media/

https://www.pinterest.ca/lulamulala/blackpink-rose/photoshoot/

https://www.pinterest.ca/lulamulala/blackpink-lisa/social-media/

https://www.pinterest.ca/lulamulala/blackpink-lisa/photoshoot/

In [1]:
import fastai.vision
from fastai.vision import *
from fastai.metrics import *
import numpy as np
import os
import re
import shutil
import cv2 as cv
import matplotlib.pyplot as plt
from pathlib import Path
In [2]:
classes = ["jisoo", "jennie", "rose", "lisa"]
path = Path('data')
In [ ]:
frontal_face_cascade = cv.CascadeClassifier(cv.data.haarcascades + "haarcascade_frontalface_default.xml")
In [ ]:
original_path = Path("original_data")
for c in classes:
    n=0
    print(c)
    orig = original_path/c
    image_paths = orig.ls()
    for image_path in image_paths:
        image = cv.imread(image_path.as_posix(), cv.COLOR_BGR2GRAY)
        if image is None: continue
        short_dim = np.min(image.shape[0:2])
        long_dim = np.max(image.shape[0:2])
        
        frontal_faces = frontal_face_cascade.detectMultiScale(gray, 1.3, 5)
        
        #want 200 frontal_face-containing and somewhat square images per class
        if len(frontal_faces)==1 and long_dim/short_dim<1.2 and n<=200:
            n+=1
            target = "data/valid/"+ c + "/" + c + "_" + str(n) + "_" + image_path.name
            print(target)
            
        else:
            target = "data/train/"+ c + "/" + c + "_" + image_path.name
            print(target)
#         shutil.copy(image_path.as_posix(), target)
# I wanted to clean the entire dataset, but I guess its okay if the training set is noisy.
#                 center = (frontal_faces[0][1] + frontal_faces[0][3],
#                           frontal_faces[0][0] + frontal_faces[0][2])
#             elif len(frontal_faces)>1:
#                 print(image_path.as_posix() + " MANY FACES")
#             elif len(frontal_faces)==0:
                
#         if np.argmin(image.shape[0:2]) == 0: #image is wide
In [3]:
transforms = get_transforms(
    do_flip = True, #D
    flip_vert = False,
    max_zoom = 1.1, #default 1.1
    max_rotate = 10, #default 10
    max_lighting = 0.2, #default 0.2
    max_warp = 0.1, #default 0.1
    p_affine = 0.75, #D
    p_lighting = 0.75) #D
In [4]:
torch.cuda.set_device(1)
In [5]:
np.random.seed(42)

data = (ImageDataBunch
        .from_folder(path,
                     train="train", valid="valid", bs=32,
                     ds_tfms=transforms, size=336)
#         .filter_by_func(lambda fname:"Blackpink" in Path(fname).as_posix())
        .normalize(imagenet_stats))
In [6]:
data.show_batch(rows=3, figsize=(7,8), ds_type=DatasetType.Valid)
In [7]:
data.show_batch(rows=3, figsize=(7,8))
In [8]:
learn = cnn_learner(data, models.resnet50, metrics=error_rate)
In [9]:
lr_find(learn)
learn.recorder.plot()
LR Finder is complete, type {learner_name}.recorder.plot() to see the graph.
In [11]:
lr=5e-3
learn.fit_one_cycle(10, lr)
epoch train_loss valid_loss error_rate time
0 0.869362 0.814787 0.305970 03:13
1 0.672895 0.744698 0.279851 03:02
2 0.635237 0.698631 0.266169 03:04
3 0.616676 0.680642 0.258706 02:57
4 0.528984 0.730667 0.269901 03:08
5 0.464990 0.798857 0.287313 02:55
6 0.378535 0.550556 0.194030 03:01
7 0.327707 0.355016 0.129353 02:56
8 0.299065 0.347908 0.116915 02:56
9 0.246048 0.354680 0.110697 02:57
In [12]:
learn.recorder.plot_losses()
In [21]:
learn.show_results(rows=10, shuffle=True)
In [13]:
interp = ClassificationInterpretation.from_learner(learn)
losses,idxs = interp.top_losses()
In [14]:
interp.plot_confusion_matrix()
In [15]:
interp.plot_top_losses(20, figsize=(15,11))
In [16]:
interp.most_confused(min_val=2)
Out[16]:
[('rose', 'jennie', 18),
 ('rose', 'jisoo', 14),
 ('jennie', 'jisoo', 10),
 ('jisoo', 'jennie', 10),
 ('lisa', 'jennie', 9),
 ('jennie', 'lisa', 6),
 ('lisa', 'jisoo', 5),
 ('lisa', 'rose', 5),
 ('jisoo', 'rose', 4),
 ('rose', 'lisa', 4),
 ('jennie', 'rose', 3)]
In [18]:
model_path = Path("../../models")
learn.save(model_path/"2019-06-28_RESNET50_10epoch_0.11error")
In [37]:
image_paths = Path("predict/nick_wang").ls()
In [50]:
for image_path in image_paths:
    image = open_image(image_path)
    pred_class,pred_idx,outputs = learn.predict(image)
    
    title = str(pred_class) + ". Probability = " + str(outputs[pred_idx.item()].item())
    image.show(figsize=(4,4), title=title)
In [53]:
!jupyter nbconvert blackpink-classifier --to html --output nbs/2019-06-28_RESNET50_0.11error
[NbConvertApp] Converting notebook blackpink-classifier.ipynb to html
[NbConvertApp] Writing 6025114 bytes to nbs/2019-06-28_RESNET50_0.11error.html
In [ ]: